﻿using System;
using System.Collections.Generic;
using System.Text;

namespace Weazel.Willows.Model
{
	public partial class QueueConnectorWriterInterface
		: QueueWriterInterface, IQueueConnectorWriter
	{
		public override void WriteChannelHierarchy(System.CodeDom.Compiler.IndentedTextWriter textWriter)
		{
			textWriter.WriteLine("Queue connector writer '{0}' parent '{1}'", LocalName, Parent.LocalName);
			textWriter.WriteLine("+---------------------------------------------------------------------+");

			textWriter.WriteLine("Attached Queue: {0}", attachedQueue.LocalName);

			textWriter.WriteLine("Writer:");
			textWriter.Indent++;

			if (this.writer != null)
				this.writer.WriteChannelHierarchy(textWriter);
			else
				textWriter.WriteLine("NULL");

			textWriter.Indent--;
		}
	
		private IQueueWriterInterface writer;
		public IQueueWriterInterface Writer
		{
			get { return writer; }
		}

		public override void BindWriter(IQueueWriterInterface writer)
		{
			if (this.writer != null)
				throw new Exception("Writer already bound");

			this.writer = writer;
		}

		public void BindReader(IQueueReaderInterface reader)
		{
			throw new NotSupportedException();
		}

		public void UnbindWriter(IQueueWriterInterface writer)
		{
			if (this.writer != writer)
				throw new Exception(string.Format("UnbindWriter(): No such writer (writer '{0}', connector: '{1}', Parent: '{2}').", writer.Name, Name, Parent.ToString()));

			this.writer = null;
		}

		public void UnbindReader(IQueueReaderInterface reader)
		{
			throw new NotSupportedException();
		}

		public override Module GetWritingDatapath()
		{
			if (writer == null)
			{
				string message =
					string.Format("No 'writer' set for Queue connector reader '{0}'", Name);

				throw new NullReferenceException(message);
			}

			return writer.GetWritingDatapath();
		}

		public override List<Module> GetReadingDatapaths()
		{
			return attachedQueue.GetReadingDatapaths();
		}

		public override bool HasAttachedChannel
		{
			get { return attachedQueue != null; }
		}

		public override bool HasAttachedWriter
		{
			get { return writer != null; }
		}

		public void DatapathMovedDownInHierarchy(Module newParent, Module oldParent)
		{
			if (attachedQueue is Queue)
			{
				Queue queue = attachedQueue as Queue;

				QueueConnectorWriterInterface newWriterConnector =
					new QueueConnectorWriterInterface(
						newParent, 
						TypeRegistry, 
						Type,
						UniqueName,
						UniqueName
					);

				newParent.Connectors.Add(newWriterConnector.LocalName, newWriterConnector);

				queue.UnbindWriter(this);
				queue.BindWriter(newWriterConnector);

				newWriterConnector.attachedQueue = queue;
				newWriterConnector.BindWriter(this);

				attachedQueue = newWriterConnector;
			}
			else if (attachedQueue is QueueConnectorWriterInterface)
			{
				throw new NotImplementedException();
			}
			else
			{
				throw new NotSupportedException();
			}
		}

		/// <summary>
		/// This method should be called whenever the datapath to which
		/// this connector is attached is moved up or down in the hierarchy.
		/// The method will make the necessary changes to the channel hierarchy.
		/// </summary>
		/// <param name="newParent">The new parent of the datapath to which 
		/// this connector is attached</param>
		/// <param name="oldParent">The old parent of the datapath to which 
		/// this connector is attached</param>
		public void DatapathMovedUpInHierarchy(Module newParent, Module oldParent)
		{
			// the parent of this connector is the datapath being
			// moved. 

			if (this.attachedQueue is Queue)
			{
				Queue queue = this.attachedQueue as Queue;
			}
			else if (this.attachedQueue is QueueConnectorWriterInterface)
			{
				QueueConnectorWriterInterface writerConnector =
					this.attachedQueue as QueueConnectorWriterInterface;

				if (writerConnector.Parent == oldParent)
				{
					// if this writer connector is connected to another 
					// writer connector located on the old parent datapath
					// then the connector on the old parent datapath is no 
					// longer needed -> remove it

					if (!oldParent.Connectors.Remove(writerConnector.LocalName))
						throw new Exception("unable to remove connector");

					writerConnector.attachedQueue.UnbindWriter(writerConnector);
					writerConnector.attachedQueue.BindWriter(this);
					attachedQueue = writerConnector.attachedQueue;

					//UnbindWriter(writerConnector);
					//BindWriter(writerConnector.Writer);
					//writerConnector.Writer.AttachedQueue = this;
				}
				else
				{
					// if this wrinter connector is NOT connected to another
					// writer connector located on the old parent datapath
					// then we ASSUME that the writer connector belongs to another
					// child datapath of the old parent datapath. In this case
					// we need to insert a new writer connector on the old parent
					// datapath between the connector on the child and the present
					// writer connector.

					QueueConnectorWriterInterface newWriterConnector =
						new QueueConnectorWriterInterface(
							oldParent, 
							TypeRegistry, 
							Type, 
							UniqueName,
							UniqueName
						);

					UnbindWriter(writerConnector);
					BindWriter(newWriterConnector);
					writerConnector.attachedQueue = newWriterConnector;
					newWriterConnector.BindWriter(writerConnector);
					newWriterConnector.attachedQueue = this;
				}
			}
			else if (this.attachedQueue is QueueWriterInterface)
			{
				// this connector was attached to a writer interface
				// in the 'old' datapath. We insert a new writer connector
				// on the old .....

				QueueWriterInterface writerInterface =
					this.attachedQueue as QueueWriterInterface;

				// create a new writer connector on the old parent 
				// datapath
				QueueConnectorWriterInterface writerConnector =
					new QueueConnectorWriterInterface(
						oldParent, 
						TypeRegistry, 
						Type,
						UniqueName,
						UniqueName
					);

				// Insert the new connector inbetween this connector and 
				// the writer interface
				UnbindWriter(writerInterface);
				BindWriter(writerConnector);
				writerConnector.BindWriter(writerInterface);
				writerInterface.AttachedQueue = writerConnector;
			}
			else
			{
				throw new NotSupportedException();
			}
		}

		#region Constructors
		public QueueConnectorWriterInterface(Module parent, TypeRegistry typeRegistry, Type type, string name, string uniqueName)
			: base(parent, typeRegistry, type, name, uniqueName)
		{
			System.Diagnostics.Trace.Assert(type != null);
		}
		#endregion
	}
}
